/* Copyright 2009 Anthony Surma
This file is part of Java's Kademlia Extension (JKADX).

JKADX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.

JKADX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with JKADX.  If not, see <http://www.gnu.org/licenses/>.
 */

package jkadx.dht;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertArrayEquals;
import org.junit.Test;

/**
 *
 * @author Anthony Surma
 */
public class TestDHT implements DHTInterface
{
    final static int TIMEOUT = 300;

    final static boolean RUN_PERFORMANCE_TESTS = true;
    final static int REPETITIONS = 1;
    final static int NUM_THREADS_REALWORLD_TESTS = 85000;
    final static int TIME_MS =0 ; // accurate to within ~1 ms (closer to +1 ms, if > 0)
    final static int TIME_NS = 0; // clock is not accurate enough, so leave at 0
    final static int CHANCE_TO_ADD_EXISTING_CONTACT = 50; // 0 - 100
    
    final static boolean RUN_CORRECTNESS_TESTS = true;
    final static int NUM_THREADS = 200000;
    volatile static int isConnected; // negative = never, 0 = maybe, positive = always
    static Random rand = UniqueId.RAND;
    final static AtomicInteger updatedCount = new AtomicInteger(0);
    final static AtomicInteger notUpdatedCount = new AtomicInteger(0);
    final static AtomicInteger connectedCount = new AtomicInteger(0);
    final static AtomicInteger notConnectedCount = new AtomicInteger(0);
    final static AtomicInteger isConnectedMethodUseCount = new AtomicInteger(0);
    private final static byte[] IP_ADDRESS1 = { 123, 123, 123, 123 };
    private final static byte[] IP_ADDRESS2 = { 123, 123, 123, 122 };  

    //* Interleaves:  1) Adding existingContacts with random UniqueIds, Random IP Addresses,
    //                and Random Ports.
    //                2) Finding the closest nodes to a random UniqueId
    //
    @Test
    public void testPerformance1()
    {
        if (!RUN_PERFORMANCE_TESTS) return;
	final int numThreads = NUM_THREADS_REALWORLD_TESTS;
        final UniqueId uniqueId = UniqueId.newRandom();
        final DHT karray = new DHT(this, uniqueId);
        final int chanceOfContactExisting = CHANCE_TO_ADD_EXISTING_CONTACT;
        final Contact[] existingContacts = new Contact[50];
        final List<Contact[]> list = Collections.synchronizedList(new ArrayList<Contact[]>());
        final Random random = rand;        
        int num = 0;

	class TestThread1 implements Runnable
	{
	    @Override
	    public void run()
	    {
                if ((random.nextInt(99) + 1) <= chanceOfContactExisting)
                {
                    Contact c = existingContacts[random.nextInt(50)];

                    if (karray.updateOrAddNew(new Contact(c.uniqueId, c.ipAddress, c.port)))
                    {
                        updatedCount.incrementAndGet();
                    }
                    else
                    {
                        notUpdatedCount.incrementAndGet();
                    }
                }
                else
                {

                    if (karray.updateOrAddNew(new Contact(UniqueId.newRandom(), IP_ADDRESS1, rand.nextInt())))
                    {
                        updatedCount.incrementAndGet();
                    }
                    else
                    {
                        notUpdatedCount.incrementAndGet();
                    }
                }
	    }
	}

        class TestThread2 implements Runnable
	{
	    @Override
	    public void run()
	    {
		Contact[] cbd = karray.getKClosestContactsTo(UniqueId.newRandom());
	        list.add(cbd);
                
	    }
	}

        karray.getAndResetUpdatedWithoutCheckingConnectionCount();
	karray.getAndResetTriedToGetKBucketForMyNodeIdCount();
        karray.getAndResetAddedEqualContactToKBucket();
        karray.getAndResetDiscardedCount();
        karray.getAndResetUpdatedWithoutCheckingConnectionCount();
        karray.triedToGetKBucketForMyNodeIdCount.getAndSet(0);        
        updatedCount.set(0);
	notUpdatedCount.set(0);
	connectedCount.set(0);
	notConnectedCount.set(0);
	isConnectedMethodUseCount.set(0);
        isConnected = 0; // multithreaded test (can set to 0)
        int discardedcount = 0;     
        //ExecutorService threadPool1 = Executors.newFixedThreadPool(60);
        //ExecutorService threadPool2 = Executors.newFixedThreadPool(60);

        for (int i = 0; i < KBucket.K; i++)
            karray.updateOrAddNew(new Contact(UniqueId.newRandom(), IP_ADDRESS1, rand.nextInt()));
        updatedCount.incrementAndGet();
        for (int i = 0; i < 50; i++)
        {
            existingContacts[i] = new Contact(UniqueId.newRandom(), IP_ADDRESS1, rand.nextInt());
        }
        try
        {
            while (num < REPETITIONS)
            {
                ExecutorService threadPool1 = Executors.newCachedThreadPool();
                ExecutorService threadPool2 = Executors.newCachedThreadPool();
                for (int i = 0; i < numThreads; i++)
                {
                    Thread.sleep(TIME_MS, TIME_NS);
                    if (rand.nextBoolean())
                    {
                        threadPool1.execute(new TestThread1());
                    }
                    else
                    {
                        threadPool2.execute(new TestThread2());
                    }
                }
                discardedcount += karray.getAndResetDiscardedCount();
                num++;
                threadPool1.shutdown();
                threadPool2.shutdown();
                threadPool1.awaitTermination(1, TimeUnit.HOURS);
                threadPool2.awaitTermination(1, TimeUnit.HOURS);
            }

            
            //System.out.println(DHT.getSizeTest());
            //System.out.println(list.size());

            //System.out.println("\nActual Updated Count           : " + (updatedCount.getFrom() - discardedcount));
            //System.out.println("Not Updated Count              : " + notUpdatedCount.getFrom());
	    //System.out.println("Discarded Count                : " + discardedcount);
	    //System.out.println("Connected Count                : " + connectedCount.getFrom());
	    //System.out.println("Not Connected Count            : " + notConnectedCount.getFrom());
            //System.out.println("Contacts Still Locked Count    : " + DHT.getNumLockedContacts());
            //System.out.println("Size of Replace Lists          : " + DHT.getSizeOfReplaceListsTest());
            assertEquals("getSize not working properly.", karray.getSizeTest(), karray.size());
            assertEquals("Failed to add correct number of existingContacts",
                notUpdatedCount.get(), connectedCount.get());
            assertEquals("Failed to add correct number of existingContacts",
                (1 + numThreads + numThreads * (num - 1)) - (notUpdatedCount.get() + updatedCount.get()), list.size());
            assertEquals("Failed to account for what it did.",
                (1 + numThreads + numThreads * (num - 1)), updatedCount.get() + notUpdatedCount.get() + list.size());
            assertEquals("Failed to unlock all contacts.", 0, karray.getNumLockedContacts());
            assertEquals("Failed to account for all contacts marked for replacement", 0, karray.getSizeOfReplaceListsTest());
        } catch (InterruptedException ex) {assertFalse("Should never see this", true);}
        karray.getAndResetUpdatedWithoutCheckingConnectionCount();
        karray.getAndResetTriedToGetKBucketForMyNodeIdCount();
        karray.getAndResetAddedEqualContactToKBucket();
        karray.getAndResetDiscardedCount();
        karray.getAndResetUpdatedWithoutCheckingConnectionCount();
        karray.triedToGetKBucketForMyNodeIdCount.getAndSet(0);
        updatedCount.set(0);
        notUpdatedCount.set(0);
        connectedCount.set(0);
        notConnectedCount.set(0);
        isConnectedMethodUseCount.set(0);
    }
   /*
    * author CD
    *
    */
    @Test
    public void TestCase1()
    {

        UniqueId uniqueid= new UniqueId ();
        DHT karray = new DHT(this, uniqueid);

        Contact ToBeAdded= new Contact(UniqueId.newRandom(), IP_ADDRESS1, 3454);
        Contact ToBeAdded2=new Contact(ToBeAdded.uniqueId,IP_ADDRESS2, 1234 );
        UniqueId uniqueid2 = ToBeAdded.uniqueId;

        karray.updateOrAddNew(ToBeAdded);
        int i= karray.size();

        //System.out.println("size is"+i);
        karray.updateOrAddNew (ToBeAdded2);
        int j=karray.size();
        assertEquals ("Failed to update and added new", j, 1);

        Contact ToBeAdded3= new Contact (UniqueId.newRandom(), IP_ADDRESS2,5435);
        karray.updateOrAddNew(ToBeAdded3);

        //Updates if UniqueId is different, but IP Address and Port is same, Does not add
        //Adds if UniqueId is diffrent, IP Address is same , Port is different

         int k=karray.size();
         assertEquals ("Failed to update and added new", k, 2);
         //GetKClosestContactsTo
         UniqueId uniqueid_1= new UniqueId ("100");
         UniqueId uniqueid_2= new UniqueId ("010");
         UniqueId uniqueid_3= new UniqueId ("110");
         UniqueId uniqueid_4= new UniqueId ("1101");
         Contact contact_1= new Contact (uniqueid_1, IP_ADDRESS1, 1234);
         Contact contact_2= new Contact (uniqueid_2, IP_ADDRESS2, 1234);
         Contact contact_3= new Contact (uniqueid_3, IP_ADDRESS1, 1234);
         Contact contact_4= new Contact (uniqueid_4, IP_ADDRESS2, 1234);
         Contact[] contacts;

         karray.updateOrAddNew(contact_1);
         karray.updateOrAddNew(contact_2);
         karray.updateOrAddNew(contact_3);
         karray.updateOrAddNew(contact_4);

        contacts= karray.getKClosestContactsTo(uniqueid_1);
        int a= contacts.length;
        //System.out.println(a);
        
        //System.out.println(contacts[5]);
        //Closest Contacts will also include the ones with random UniqueIds
        assertEquals("Failed to get the Closest", contacts[0], contact_1);
        assertEquals("Failed to get the Closest Second", contacts[1], contact_3);
        assertEquals("Failed to get the Closest Third", contacts[2], contact_2);

        //TestGetRandomUniqueIdFromBucketPosition
        UniqueId ID= karray.getRandomUniqueIdFromKBucket(2);
        //System.out.println(ID);

        UniqueId uniqueid_new  = new UniqueId("0000010011000");
        UniqueId uniqueid_new2 = new UniqueId("0000001001000");

        int[] xord2= new int[UniqueId.LENGTH];

        int index=0;
        while(index<UniqueId.LENGTH)
        {
         xord2[index] = uniqueid_new.bits[index] ^ uniqueid_new2.bits[index];
         index++;
        }

        //System.out.println(new UniqueId(xord2));
        DHT new_dht = new DHT(this,new UniqueId(xord2));
        int m= uniqueid_new.getMSBAfterXoring(uniqueid_new, uniqueid_new2);
        UniqueId uid= new_dht.getRandomUniqueIdFromKBucket(151);
        //System.out.println(uid);
        //System.out.println(s);
        //System.out.println(new_dht.myUniqueId);
        //System.out.println(uid);

    }

    @Test
    public void testGetKClosest()
    {
        if (!RUN_CORRECTNESS_TESTS) return;
	isConnected = -1;
	int NBITS = UniqueId.NBITS;
	UniqueId uniqueId = new UniqueId();
	DHT karray = new DHT(this, uniqueId);
	Contact contact1;
	Contact contact2;
	Contact contact3;
	Contact contact4;
	Contact contact5;
	Contact contact6;
	Contact[] contacts;
	Contact expectedcontact;
	Contact obtainedcontact;
	UniqueId UniqueId1 = new UniqueId(); // zero
	UniqueId UniqueId2 = new UniqueId("001"); // expected
	UniqueId UniqueId3 = new UniqueId("010");
	UniqueId UniqueId4 = new UniqueId("011");
	UniqueId UniqueId5 = new UniqueId("100");
	UniqueId UniqueId6 = new UniqueId("101");
	int num;
	int expectedint;
	int obtainedint;

	//UniqueId1 = .....0 // shouldn't be added because it's equal to myuniqueid (all zeroes)
	//UniqueId2 = .....1 // kbucket-30 = K-1
	//UniqueId3 = ....10 // kbucket-29 = K-2
	//UniqueId4 = ....11 // kbucket-29 = K-2
	//UniqueId5 = ...100 // kbucket-28 = K-3
	//UniqueId6 = ...101//  kbucket-28 = K-3

	// 0123
	// 0000 myuniqueid
	// 0001 contact
	// 0001 xord
	// finds a match in kbucket3 for contact2
	// there's no more 1's, and still need 2 more contacts
	// 1110 flipped
	// 0111 reversed
	// K = 4, i = next truebit, so looks in K - i - 1 => 4 - 1 - 1 = 2
	//UniqueId2.set(NBITS - 1);
	//UniqueId3.set(NBITS - 2);
	//UniqueId4.set(NBITS - 1); UniqueId4.set(NBITS - 2);
	//UniqueId5.set(NBITS - 3);
	//UniqueId6.set(NBITS - 1); UniqueId6.set(NBITS - 3);
	contact1 = new Contact(UniqueId1, IP_ADDRESS1, 64233);
	contact2 = new Contact(UniqueId2, IP_ADDRESS2, 64233);
	contact3 = new Contact(UniqueId3, IP_ADDRESS1, 64233);
	contact4 = new Contact(UniqueId4, IP_ADDRESS1, 64233);
	contact5 = new Contact(UniqueId5, IP_ADDRESS1, 64233);
	contact6 = new Contact(UniqueId6, IP_ADDRESS1, 64233);

	karray.updateOrAddNew(contact1);
	karray.updateOrAddNew(contact2);
	karray.updateOrAddNew(contact3);
	karray.updateOrAddNew(contact4);
	karray.updateOrAddNew(contact5);
	karray.updateOrAddNew(contact6);

	expectedint = 5;
	obtainedint = karray.size();
	assertEquals("Failed to get proper result for total size #1.",
		expectedint, obtainedint);

	contacts = karray.getKClosestContactsTo(UniqueId1); // ...000
	if (DHT.K > expectedint)
	{
	    num = expectedint;
	}
	else
	{
	    num = DHT.K;
	}
	expectedint = num;
	obtainedint = contacts.length;
	assertEquals("Failed to return correct number of closest contacts #2.", 
		expectedint, obtainedint);

	contacts = karray.getKClosestContactsTo(UniqueId1); // ...000
	expectedcontact = contact2;
	obtainedcontact = contacts[0];
	assertNotNull("Failed to return not null contact.", obtainedcontact);
	assertEquals("Failed to return correct first closest contact #2A.",
		expectedcontact.toString(), obtainedcontact.toString());
	expectedcontact = contact3;
	obtainedcontact = contacts[1];
	assertEquals("Failed to return correct second closest contact #2B.",
		expectedcontact.toString(), obtainedcontact.toString());
	expectedcontact = contact4;
	obtainedcontact = contacts[2];
	assertEquals("Failed to return correct third closest contact #2C.",
		expectedcontact.toString(), obtainedcontact.toString());

	contacts = karray.getKClosestContactsTo(UniqueId2); // ...001
	expectedcontact = contact2;
	obtainedcontact = contacts[0];
	assertEquals("Failed to return first correct closest contact #3A.",
		expectedcontact.toString(), obtainedcontact.toString());
	expectedcontact = contact4;
	obtainedcontact = contacts[1];
	assertEquals("Failed to return second correct closest contact #3B.",
		expectedcontact.toString(), obtainedcontact.toString());
	expectedcontact = contact3;
	obtainedcontact = contacts[2];
	assertEquals("Failed to return second correct closest contact #3B.",
		expectedcontact.toString(), obtainedcontact.toString());

	contacts = karray.getKClosestContactsTo(UniqueId3); // ...010
	expectedcontact = contact3;
	obtainedcontact = contacts[0];
	assertEquals("Failed to return correct first closest contact #4A.",
		expectedcontact.toString(), obtainedcontact.toString());
	expectedcontact = contact4;
	obtainedcontact = contacts[1];
	assertEquals("Failed to return correct second closest contact #4B.",
		expectedcontact.toString(), obtainedcontact.toString());
	// would be contact1, but it was never added because it = myuniqueid
	expectedcontact = contact2;
	obtainedcontact = contacts[2];
	assertEquals("Failed to return correct second closest contact #4C.",
		expectedcontact.toString(), obtainedcontact.toString());
	

	contacts = karray.getKClosestContactsTo(UniqueId4); // ...011
	expectedcontact = contact4;
	obtainedcontact = contacts[0];
	assertEquals("Failed to return correct closest contact #5.",
		expectedcontact.toString(), obtainedcontact.toString());
	

	contacts = karray.getKClosestContactsTo(UniqueId5); // ...100
	expectedcontact = contact5;
	obtainedcontact = contacts[0];
	assertEquals("Failed to return correct closest contact #6.",
		expectedcontact.toString(), obtainedcontact.toString());


	contacts = karray.getKClosestContactsTo(UniqueId6); // ...101
	expectedcontact = contact6;
	obtainedcontact = contacts[0];
	assertEquals("Failed to return correct closest contact #7.",
		expectedcontact.toString(), obtainedcontact.toString());
	
    }

    @Test
    public void testUpdate()
    {
        if (!RUN_CORRECTNESS_TESTS) return;
	final UniqueId nodeid1 = UniqueId.newRandom();
	DHT karray = new DHT(this, nodeid1);
	int expectedint;
	int obtainedint;
	UniqueId nodeid2 = UniqueId.newRandom();
	Contact test;
	Contact expectedcontact;
	Contact obtainedcontact;
	Contact[] contacts;
	Contact contact1 = new Contact(nodeid1, IP_ADDRESS1, 1);
	Contact contact2 = new Contact(nodeid2, IP_ADDRESS1, 2);
	boolean wasUpdated;

	// shouldn't ever add my own nodeid to any kbucket
	expectedint = 0;
	obtainedint = karray.size();
	assertEquals("Failed to get proper result for total size of empty DHT #1.",
		expectedint, obtainedint);
	
	karray.updateOrAddNew(contact1);
	expectedint = 0;
	obtainedint = karray.size();
	assertEquals("Failed to not add my own nodeid to kbuckets. #2",
		expectedint, obtainedint);

	// should add a contact if it doesn't exist in the kbucket
	karray.updateOrAddNew(contact2);
	expectedint = 1;
	obtainedint = karray.size();
	assertEquals("Failed to add a new contact to kbuckets. #3",
		expectedint, obtainedint);

	// should not add a contact that already exists
	test = new Contact(contact1.uniqueId, IP_ADDRESS1, 0);
	karray.updateOrAddNew(test);
	expectedint = 1;
	obtainedint = karray.size();
	assertEquals("Failed to not add an existing contact to kbuckets. #3",
		expectedint, obtainedint);

	karray = new DHT(this, nodeid1);

        // <editor-fold defaultstate="collapsed" desc="Thought process for this test...">
        // first I have to fill up a kbucket
        // If it weren't for a maxksize, the following buckets could contain:
        // Where K = NBITS, the K - 1 kbucket  2^0 = 1
        //                      K - 2 kbucket  2^1 = 2
        //                      K - 3 kbucket  2^2 = 4
        //                      K - 4 kbucket  2^3 = 8
        //                      K - 5 kbucket  2^4 = 16
        //                      K - 6 kbuckets 2^5 = 32 > 20
        // so we'll work with (K - 6)th kbucket, which means that the first
        // bit that's set to true in the nodeid.  No matter what the following
        // bits are, all those nodeids will go into that bucket.

        // so I need 20 different K size UniqueIds with their (K - 6)th bit
        // set, while the others are different.// </editor-fold>
	contacts = new Contact[20];
	contacts[ 0] = new Contact(new UniqueId("100000"), IP_ADDRESS1,  0);
	contacts[ 1] = new Contact(new UniqueId("100001"), IP_ADDRESS1,  1);
	contacts[ 2] = new Contact(new UniqueId("100010"), IP_ADDRESS1,  2);
	contacts[ 3] = new Contact(new UniqueId("100011"), IP_ADDRESS1,  3);
	contacts[ 4] = new Contact(new UniqueId("100100"), IP_ADDRESS1,  4);
	contacts[ 5] = new Contact(new UniqueId("100101"), IP_ADDRESS1,  5);
	contacts[ 6] = new Contact(new UniqueId("100110"), IP_ADDRESS1,  6);
	contacts[ 7] = new Contact(new UniqueId("100111"), IP_ADDRESS1,  7);
	contacts[ 8] = new Contact(new UniqueId("101000"), IP_ADDRESS1,  8);
	contacts[ 9] = new Contact(new UniqueId("101001"), IP_ADDRESS1,  9);
	contacts[10] = new Contact(new UniqueId("101010"), IP_ADDRESS1, 10);
	contacts[11] = new Contact(new UniqueId("101011"), IP_ADDRESS1, 11);
	contacts[12] = new Contact(new UniqueId("101100"), IP_ADDRESS1, 12);
	contacts[13] = new Contact(new UniqueId("101101"), IP_ADDRESS1, 13);
	contacts[14] = new Contact(new UniqueId("101110"), IP_ADDRESS1, 14);
	contacts[15] = new Contact(new UniqueId("101111"), IP_ADDRESS1, 15);
	contacts[16] = new Contact(new UniqueId("110000"), IP_ADDRESS1, 16);
	contacts[17] = new Contact(new UniqueId("110001"), IP_ADDRESS1, 17);
	contacts[18] = new Contact(new UniqueId("110010"), IP_ADDRESS1, 18);
	contacts[19] = new Contact(new UniqueId("110011"), IP_ADDRESS1, 19);

	for (int i = 0; i < 20; i++)
	{
	    karray.updateOrAddNew(contacts[i]);
	}
	expectedint = 20;
	obtainedint = karray.size();
	assertEquals("Failed to add 20 new contacts to kbuckets same kbucket. #4",
		expectedint, obtainedint);

	// now, that kbucket is full.  So adding another with connection being
	// set to true should keep the old contact, and discard the new one.

	isConnected = 1; // it's alive
	test = new Contact(new UniqueId("110100"), IP_ADDRESS1, 20);
	wasUpdated = karray.updateOrAddNew(test); // side effect is that contact[0] was updated in list and now contact[1] is oldest

	assertEquals("Failed to not update when kbucket is full and oldest contact is alive. #8a",
		false, wasUpdated);

	expectedint = 20;
	obtainedint = karray.size();
	assertEquals("Failed to discard new contact when kbucket is full and oldest contact is alive. #5",
		expectedint, obtainedint);
	// prove it... oldest contact should still be in still in kbucket
	Contact[] closestmatches = karray.getKClosestContactsTo(contacts[0].uniqueId);
	expectedcontact = contacts[0];
	obtainedcontact = closestmatches[0];
	assertEquals("Failed to discard new contact when kbucket is full and oldest contact is alive. #6",
		expectedcontact.uniqueId, obtainedcontact.uniqueId);
	// prove it... new contact should not be in kbucket
	closestmatches = karray.getKClosestContactsTo(test.uniqueId);
	expectedcontact = contacts[16]; // unintuitive result
	obtainedcontact = closestmatches[0];
	assertEquals("Failed to discard new contact when kbucket is full and oldest contact is alive. #7",
		expectedcontact.uniqueId, obtainedcontact.uniqueId);

	isConnected = -1; // it's not alive
	// remember contact[1] is oldest now, because contact[0] responded to liveness check in test above
	test = new Contact(new UniqueId("110100"), IP_ADDRESS1, 20);
	wasUpdated = karray.updateOrAddNew(test);

	assertEquals("Failed to update when kbucket is full and oldest contact is dead. #8a",
		true, wasUpdated);
	
	expectedint = 20;
	obtainedint = karray.size();
	assertEquals("Failed to discard a contact when kbucket is full and oldest contact is dead. #8",
		expectedint, obtainedint);

	// prove it... new contact has to be in the kbucket
	closestmatches = karray.getKClosestContactsTo(test.uniqueId);
	expectedcontact = test;
	obtainedcontact = closestmatches[0];
	assertEquals("Failed to add new contact when kbucket is full and oldest contact is dead. #9",
		expectedcontact.uniqueId, obtainedcontact.uniqueId);

	// prove it... contact[1] should not be in the kbucket
	closestmatches = karray.getKClosestContactsTo(contacts[1].uniqueId);
	expectedcontact = contacts[0];
	obtainedcontact = closestmatches[0];
	assertEquals("Failed to discard oldest contact when kbucket is full and oldest contact is dead. #10",
		expectedcontact.uniqueId, obtainedcontact.uniqueId);
    }

    @Test
    public void testUpdateConcurrent()
    {
        if (!RUN_CORRECTNESS_TESTS) return;
	ExecutorService threadPool1 = Executors.newCachedThreadPool();
	final int numthreads = NUM_THREADS;
	final int numupdatesperthread = 1;
	UniqueId myUniqueId = new UniqueId();
	final DHT karray1 = new DHT(this, myUniqueId);
	final AtomicInteger threadCount = new AtomicInteger(0);
	int discardedcount;


	class Test implements Runnable
	{

	    @Override
	    public void run()
	    {
		
		for (int i = 0; i < numupdatesperthread; i++)
		{
		    if (karray1.updateOrAddNew(new Contact(UniqueId.newRandom(), IP_ADDRESS1, 1)))
		    {
			updatedCount.incrementAndGet();
		    }
		    else
		    {
			notUpdatedCount.incrementAndGet();
		    }
		}
		threadCount.incrementAndGet();
	    }
	}
	class Test2 implements Runnable
	{
	    final Contact contact;

	    Test2(Contact contact)
	    {
		this.contact = contact;
	    }

	    @Override
	    public void run()
	    {
		for (int i = 0; i < numupdatesperthread; i++)
		{
		    if (karray1.updateOrAddNew(contact))
		    {
			updatedCount.incrementAndGet();
		    }
		    else
		    {
			notUpdatedCount.incrementAndGet();
		    }
		}
	    }
	}

            updatedCount.set(0);
	    notUpdatedCount.set(0);
	    connectedCount.set(0);
	    notConnectedCount.set(0);
	    isConnectedMethodUseCount.set(0);
            
	isConnected = -1;	
	int added = 0;
	Contact[] contacts = new Contact[UniqueId.NBITS * 20];
	for (int i = 0; i < 5; i++) // Fills all kbuckets
	{
	    for (int j = 0; j < 32; j++)
	    {
		int temp[] = {0, 0, 0, 0, 0};
		temp[i] = 0x80000000 >>> j;		
		for (int k = 0; k < 20; k++)
		{
		    contacts[added] = new Contact(new UniqueId(temp), IP_ADDRESS1, k);
		    temp[4] += 1;
		    karray1.updateOrAddNew(contacts[added]);
		    added++;
		}
	    }
	}
	assertEquals("Failed to return full",
		true, karray1.isFull());

	//System.out.println(karray1.size());
	assertEquals("Failed to add correct number of contacts",
		(UniqueId.NBITS * 20) - 69, karray1.size());

	assertEquals("Failed to add correct number of contacts",
		(UniqueId.NBITS * 20) - 69, karray1.getSizeTest());

	int blah; // fringe cases
	int blah2;
	for (int ii = 0; ii < 1; ii++) // number of time to run this test
	{
	    threadCount.set(0);
	    isConnected = 0;
	    updatedCount.set(0);
	    notUpdatedCount.set(0);
	    connectedCount.set(0);
	    notConnectedCount.set(0);
	    isConnectedMethodUseCount.set(0);
            karray1.getAndResetUpdatedWithoutCheckingConnectionCount();
	    karray1.getAndResetTriedToGetKBucketForMyNodeIdCount();
	    
	    threadPool1 = Executors.newCachedThreadPool();
	    for (int i = 0; i < numthreads; i++)
	    {
		threadPool1.execute(new Test());
	    }
	    threadPool1.shutdown();
	    try
	    {
		threadPool1.awaitTermination(1, TimeUnit.HOURS);
	    }
	    catch (InterruptedException e)
	    {
		assertFalse("Should never see this", true);
	    }
	    assertEquals("Failed to add correct number of contacts",
		    (UniqueId.NBITS * 20) - 69, karray1.size());
	    assertEquals("Failed to add correct number of contacts",
		    (UniqueId.NBITS * 20) - 69, karray1.getSizeTest());

	    discardedcount = karray1.getAndResetDiscardedCount();
	    blah = karray1.getAndResetUpdatedWithoutCheckingConnectionCount();
	    blah2 = karray1.getAndResetTriedToGetKBucketForMyNodeIdCount();
	   // System.out.println("\nUpdated Count             : " + (updatedCount.getFrom()));
	   // System.out.println("Not Updated Count         : " + notUpdatedCount.getFrom());
	   // System.out.println("\nDiscarded Count           : " + discardedcount);
	   // System.out.println("Connected     Count       : " + connectedCount.getFrom());
	   // System.out.println("Not Connected Count       : " + notConnectedCount.getFrom());
	   // System.out.println("blah                      : " + blah + "\n");
	    //System.out.println("isConnected()Use Count    : " + isConnectedMethodUseCount.getFrom() + "\n");
	    //System.out.println("Updated + Not Updated     : " + (updatedCount.getFrom() + notUpdatedCount.getFrom()));
	    //System.out.println("Updated - Discardard Count: " + (updatedCount.getFrom() - discardedcount));

	    assertEquals("'Something' went horribly wrong", (numthreads * numupdatesperthread), (updatedCount.get() + notUpdatedCount.get()));

	    assertEquals("Failed to add correct number of contacts", (UniqueId.NBITS * 20) - 69, karray1.size());

	    // sometimes a contact is not updated when it's not connected for reasons other than blah
	    // so... when does DHT.updateOrAddNew return false?  Solved with getAndResetTriedToGetKBucketForMyNodeIdCount()

	    assertEquals("Failed to account for discarded updates: " + "blah : " + blah + ", " + "connectedCount: " + connectedCount.get() + ", " + "notUpdatedCount: " + notUpdatedCount.get() + ", " + "isConnectedMethodUseCount.get(): " + isConnectedMethodUseCount.get() + ", " + "discardedcount: " + discardedcount + ", " + "getAndResetTriedToGetKBucketForMyNodeIdCount(): " + blah2,
		    numthreads * numupdatesperthread, isConnectedMethodUseCount.get() + discardedcount + blah + blah2);

	    assertEquals("Failed to not update any contacts that were connected: blah: " + blah + " blah2: " + blah2,
		    connectedCount.get() + blah2, notUpdatedCount.get());

	    assertEquals("Something else went horribly wrong... I don't know what causes this very intermittant bug... It's most likely an\n" + "artifact caused by the test itself and can be ignored.  It happens with a frequency less than 1 per million chance.\n" + "What appears to be happening is that one (and only one) thread gets 'lost' somewhere... but the simplest explanations\n" + "are that:\n 1) one of the counts isn't getting updated when it should.\n2) The last thread doesn't complete before shuttdown.\n" + "\nDiscarded Count           : " + discardedcount + "\n" + "Connected     Count       : " + connectedCount.get() + "\n" + "Not Connected Count       : " + notConnectedCount.get() + "\n" + "blah                      : " + blah + "\n" + "blah2                      : " + blah2 + "\n" + "actual threadCount         : " + threadCount.getAndSet(0) + "\n",
		    (numthreads * numupdatesperthread), connectedCount.get() + notConnectedCount.get() + discardedcount + blah + blah2);

	    assertEquals("Failed to update all contacts that were not connected",
		    notConnectedCount.get(), updatedCount.get() - discardedcount - blah);
	}
	
	threadCount.set(0);
	isConnected = 1;
	isConnectedMethodUseCount.set(0);
	updatedCount.set(0);
	notUpdatedCount.set(0);
	connectedCount.set(0);
	notConnectedCount.set(0);
	threadPool1 = Executors.newCachedThreadPool();
	for (int i = 0; i < added; i++)
	{
	    threadPool1.execute(new Test2(contacts[i]));
	}
	threadPool1.shutdown();
	try
	{
	    threadPool1.awaitTermination(1, TimeUnit.HOURS);
	}
	catch (InterruptedException e) {System.out.println("Should never see this");}
	discardedcount = karray1.getAndResetDiscardedCount();
	//System.out.println("\nActual Updated Count   : " + (updatedCount.getFrom() - discardedcount));
	//System.out.println("Not Updated Count      : " + notUpdatedCount.getFrom());
	//System.out.println("Discarded Count        : " + discardedcount);
	//System.out.println("Connected     Count    : " + connectedCount.getFrom());
	//System.out.println("Not Connected Count    : " + notConnectedCount.getFrom());
	//System.out.println("isConnected()Use Count : " + isConnectedMethodUseCount.getFrom());
	//System.out.println("Actual Size of kArray  : " + karray1.getSizeTest());
    }

    @Test
    public void testUpdateConcurrent2()
    {
        if (!RUN_CORRECTNESS_TESTS) return;
	ExecutorService threadPool1 = Executors.newCachedThreadPool();
	UniqueId UniqueId = new UniqueId();
	final DHT karray = new DHT(this, UniqueId);

	class Test implements Runnable
	{
	    final int i;
	    final int j;
	    final int k;
	    final int[] id;

	    Test(int i, int j, int k, int[] id)
	    {
		this.id = Arrays.copyOf(id, 5); // has to be a copy of the array object
		this.i = i;
		this.j = j;
		this.k = k;
	    }

	    @Override
	    public void run()
	    {
		if (karray.updateOrAddNew(new Contact(new UniqueId(id), IP_ADDRESS1, k)))
		{
		    updatedCount.incrementAndGet();
		}
		else
		{
		    notUpdatedCount.incrementAndGet();
		}
	    }
	}
	
	isConnected = -1;
	updatedCount.set(0);
	notUpdatedCount.set(0);
	connectedCount.set(0);
	notConnectedCount.set(0);
	isConnectedMethodUseCount.set(0);

	for (int i = 0; i < 5; i++) // Fills all kbuckets
	{
	    for (int j = 0; j < 32; j++)
	    {
		int temp[] = {0, 0, 0, 0, 0};
		temp[i] = 0x80000000 >>> j;
		for (int k = 0; k < 20; k++)
		{
		    threadPool1.execute(new Test(i, j, k, temp));
		    temp[4] += 1;
		}
	    }
	}
	threadPool1.shutdown();
	try
	{
	    threadPool1.awaitTermination(1, TimeUnit.HOURS);
	}
	catch (InterruptedException e) {assertFalse("Should never see this", true);}

	assertEquals("Failed to add correct number of contacts...\n"
		+ "updatedCount + notupdatedCount: " + (updatedCount.get() + notUpdatedCount.get()) + "\n",
		(UniqueId.NBITS * 20) - 69, karray.size());

	isConnectedMethodUseCount.set(0);
	assertEquals("Failed to return full",
		true, karray.isFull());
	
	int discardedcount = karray.getAndResetDiscardedCount();
	//assertEquals("Failed to not discard any contacts", // shouldn't have to discard any
	//	updatedCount.getFrom() - discardedcount, 620);

	assertEquals("Failed to update all contacts that were found not to be connected",
		UniqueId.NBITS * 20, updatedCount.get());

	//System.out.println("\nActual Updated Count   : " + (updatedcount.getFrom() - discardedcount));
	//System.out.println("Not Updated Count      : " + notupdatedcount.getFrom());
	//System.out.println("Discarded Count        : " + discardedcount);
	//System.out.println("Connected     Count    : " + connectedcount.getFrom());
	//System.out.println("Not Connected Count    : " + notconnectedcount.getFrom());
    }

    @Test
    public void testGetKClosestNodesConcurrent()
    {
        if (!RUN_CORRECTNESS_TESTS) return;
	ExecutorService threadPool1 = Executors.newCachedThreadPool();
	UniqueId uniqueId = new UniqueId();
	final DHT karray = new DHT(this, uniqueId);
	final Contact[] lastContacts = new Contact[UniqueId.NBITS];
	final Contact[] closest = new Contact[UniqueId.NBITS];
	final Contact[] closestContacts = new Contact[UniqueId.NBITS];

	class Test implements Runnable
	{
	    final Contact contact;
	    final int index;


	    Test (final Contact contact, final int index)
	    {
		this.contact = contact;
		this.index = index;
	    }
	    @Override
	    public void run()
	    {
		final Contact[] cbd;
		cbd = karray.getKClosestContactsTo(contact.uniqueId);
		closest[index] = cbd[0];
	    }
	}

	
	isConnected = -1;
	updatedCount.set(0);
	notUpdatedCount.set(0);
	connectedCount.set(0);
	notConnectedCount.set(0);
	isConnectedMethodUseCount.set(0);
	
	for (int i = 0; i < 5; i++) // Fills all kbuckets
	{
	    for (int j = 0; j < 32; j++)
	    {
		int temp[] =
		{
		    0, 0, 0, 0, 0
		};
		temp[i] = 0x80000000 >>> j;
		for (int k = 0; k < 20; k++)
		{
		    lastContacts[i * 32 + j] = new Contact(new UniqueId(temp), IP_ADDRESS1, k);
		    if (karray.updateOrAddNew(lastContacts[i * 32 + j]))
		    {
			updatedCount.incrementAndGet();
		    }
		    else
		    {
			notUpdatedCount.incrementAndGet();
		    }
		    temp[4] += 1;
		}
	    }
	}
	int discardedcount = karray.getAndResetDiscardedCount();
	//System.out.println("\nActual Updated Count   : " + (updatedcount.getFrom() - discardedcount));
	//System.out.println("Not Updated Count      : " + notupdatedcount.getFrom());
	//System.out.println("Discarded Count        : " + discardedcount);
	//System.out.println("Connected     Count    : " + connectedcount.getFrom());
	//System.out.println("Not Connected Count    : " + notconnectedcount.getFrom());
	assertEquals("Failed to add correct number of contacts",
		(UniqueId.NBITS * 20) - 69, karray.size());
	
	assertEquals("Failed to not discard any contacts", // shouldn't have to discard any
		(UniqueId.NBITS * 20) - 69, updatedCount.get() - 69 - discardedcount);

	assertEquals("Failed to not check if anything was connected because nothing needed to be replaced (except for 69)", // exactly 551 was added, correct amount to each kbucket
		69, isConnectedMethodUseCount.get());

	for (int i = 0; i < UniqueId.NBITS; i++)
	{
	    threadPool1.execute(new Test(lastContacts[i], i));
	}
	threadPool1.shutdown();
	try
	{
	    threadPool1.awaitTermination(1, TimeUnit.HOURS);
	}
	catch (InterruptedException ex) {assertFalse("Should never see this", true);}
	for (int i = 0; i < UniqueId.NBITS; i++)
	{
	    closestContacts[i] = closest[i];
	   //assertEquals("Failed to getFrom closest contact when i == " + i, 0, closest[i].getDistance());  //XXX must rewrite test
	}
	assertArrayEquals("Failed to get equal arrays", lastContacts, closestContacts);
    }
    
    @Test
    public void testGetKClosestNodesConcurrent2()
    {
        if (!RUN_CORRECTNESS_TESTS) return;
	int numThreads = NUM_THREADS;
	ExecutorService threadPool1 = Executors.newCachedThreadPool();
	UniqueId uniqueId = new UniqueId();
	final DHT karray = new DHT(this, uniqueId);
	final List<Contact[]> list = Collections.synchronizedList(new ArrayList<Contact[]>(numThreads));
	
	class Test implements Runnable
	{
	    @Override
	    public void run()
	    {				
		Contact[] cbq = karray.getKClosestContactsTo(UniqueId.newRandom());
		if (cbq != null && cbq.length == DHT.K)
		{
		    list.add(cbq);
		}
	    }
	}

	// fills all kbuckets
	isConnected = -1;
	updatedCount.set(0);
	notUpdatedCount.set(0);
	connectedCount.set(0);
	notConnectedCount.set(0);
	isConnectedMethodUseCount.set(0);
	for (int i = 0; i < UniqueId.NBITS; i++)
	{
	    int temp = (int)Math.pow(2, i);
	    int num = Math.min(DHT.K, temp);
	    for (int j = temp; j < (temp + num); j++)
	    {
		if (karray.updateOrAddNew(new Contact(UniqueId.newRandom(), IP_ADDRESS1, j)))
		{
		    updatedCount.incrementAndGet();
		}
		else
		{
		    notUpdatedCount.incrementAndGet();
		}
	    }
	}
	int discardedcount = karray.getAndResetDiscardedCount();
//	System.out.println("\nActual Updated Count   : " + (updatedCount.getFrom() - discardedcount));
//System.out.println("Not Updated Count      : " + notUpdatedCount.getFrom());
	//System.out.println("Discarded Count        : " + discardedcount);
	//System.out.println("Connected     Count    : " + connectedcount.getFrom());
	//System.out.println("Not Connected Count    : " + notconnectedcount.getFrom());
	//assertEquals("Failed to add correct number of contacts",
	//	551, DHT.size());

	for (int i = 0; i < numThreads; i++)
	{
	    threadPool1.execute(new Test());
	}
	threadPool1.shutdown();
	try
	{
	    threadPool1.awaitTermination(1, TimeUnit.HOURS);
	}
	catch (InterruptedException ex) {assertFalse("Should never see this", true);}
	assertEquals("Failed to get correct number of ClosestKNodes results",
		numThreads, list.size());
    }

    @Test
    public void testUpdateAndGetClosestKNodesConcurrently()
    {
	int numThreads = NUM_THREADS;
        if (!RUN_CORRECTNESS_TESTS) return;
	ExecutorService threadPool1 = Executors.newCachedThreadPool();
	ExecutorService threadPool2 = Executors.newCachedThreadPool();
	final CountDownLatch cdl = new CountDownLatch(0);
	UniqueId uniqueId = new UniqueId();
	final DHT karray = new DHT(this, uniqueId);
	final List<Contact[]> list = Collections.synchronizedList(new ArrayList<Contact[]>(numThreads));

	class Update implements Runnable
	{
	    final int i;
	    final int j;

	    Update(int i, int j)
	    {
		this.i = i;
		this.j = j;
	    }

	    @Override
	    public void run()
	    {
		try
		{
		    cdl.await();
		}
		catch (InterruptedException ex){assertFalse("Should never see this", true);}
		if (karray.updateOrAddNew(new Contact(UniqueId.newRandom(), IP_ADDRESS1, j)))
		{
		    updatedCount.incrementAndGet();
		}
		else
		{
		    notUpdatedCount.incrementAndGet();
		}
	    }
	}
	class GetClosest implements Runnable
	{
	    @Override
	    public void run()
	    {
		try
		{
		    cdl.await();
		}
		catch (InterruptedException ex) {assertFalse("Should never see this", true);}
		Contact[] cbq = karray.getKClosestContactsTo(UniqueId.newRandom());
		list.add(cbq);
	    }
	}
	// fills all kbuckets
	isConnected = 0;
	updatedCount.set(0);
	notUpdatedCount.set(0);
	connectedCount.set(0);
	notConnectedCount.set(0);
	isConnectedMethodUseCount.set(0);
	for (int i = 0; i < UniqueId.NBITS; i++)
	{
	    int temp = (int)Math.pow(2, i);
	    for (int j = temp; j < temp + 20; j++)
	    {
		threadPool1.execute(new Update(i, j));
	    }
	}
	for (int i = 0; i < numThreads; i++)
	{
	    threadPool2.execute(new GetClosest());
	}

	cdl.countDown();
	threadPool1.shutdown();
	threadPool2.shutdown();
	try
	{
	    threadPool1.awaitTermination(1, TimeUnit.HOURS);
	    threadPool2.awaitTermination(1, TimeUnit.HOURS);
	}
	catch (InterruptedException e) {assertFalse("Should never see this", true);}
	assertEquals("Failed to get correct number of ClosestKNodes results",
		numThreads, list.size());
	
    }

    @Test
    public void testStringToUniqueIdAndViceVera()
    {
         if (!RUN_CORRECTNESS_TESTS) return;
	UniqueId uniqueId1 = UniqueId.newRandom();
	String uniqueId1ToString = uniqueId1.toString();
	UniqueId uniqueId3 = new UniqueId(uniqueId1ToString);
	UniqueId uniqueId4 = new UniqueId("1");
	UniqueId uniqueId5 = new UniqueId("000000000000000000000001");
	UniqueId uniqueId6 = new UniqueId("1");

	//System.out.println();
	//System.out.println("uniqueId1ToString: " + uniqueId1ToString);
	//System.out.println(Arrays.equals(uniqueId1.getBits(), uniqueId3.getBits()));
	//System.out.println(""+uniqueId1.getBits().length +" "+ uniqueId3.getBits().length);
	//System.out.println();

	//for (byte b : uniqueId1.getBits())
	//    System.out.print(b);
	//System.out.println();
	//for (byte b : uniqueId3.getBits())
	//    System.out.print(b);
	//System.out.println();


	//System.out.println(uniqueId1.hashCode());
	//System.out.println(uniqueId3.hashCode());
	assertEquals("Failed to insert zeroes into UniqueId", uniqueId4, uniqueId5);
	assertEquals("Failed to convert valid UniqueId to String and back again", uniqueId1, uniqueId3);
	assertEquals("Failed to convert valid UniqueId to String", uniqueId5, uniqueId6);
    }

    @Override // attempts to simulate network churn when isConnected == 0;
    public boolean isConnected(final Contact contact) 
    {
	
	boolean result = false;

	isConnectedMethodUseCount.incrementAndGet();
	if (isConnected > 0) // yes
	{
	    result = true;
	}
	else if (isConnected < 0) // no
	{
	    result = false;
	}
	else // maybe = 50% probability unconnected
	{
	    int chance = rand.nextInt(10) + 1;
	    try
	    {	
		if (chance <= 5)
		{	    
		    Thread.sleep(TIMEOUT);
		    result = false;
		}
		else if (chance > 5 && chance < 9)
		{
		    Thread.sleep(rand.nextInt(TIMEOUT/2) + rand.nextInt(TIMEOUT/2));
		    result = true;
		}
		else 
		{
		    Thread.sleep(rand.nextInt(TIMEOUT/2) + rand.nextInt(TIMEOUT/4));
		    result = true;
		}
	    }
	   catch (InterruptedException e) {assertFalse("Should never see this", true);}
	}
	if (result)
	{
	    connectedCount.incrementAndGet();
	}
	else
	{
	    notConnectedCount.incrementAndGet();
	}
	return result;
    }

    @Test
    public void testUniqueIdToBytesAndViceVersa()
    {
         if (!RUN_CORRECTNESS_TESTS) return;
	UniqueId uniqueId1 = new UniqueId();
	UniqueId uniqueId2 = new UniqueId("1");
	UniqueId uniqueId3 = new UniqueId("10");
	UniqueId uniqueId4 = new UniqueId("100");
	UniqueId uniqueId1a;
	UniqueId uniqueId2a;
	UniqueId uniqueId3a;
	UniqueId uniqueId4a;
	UniqueId randomUniqueId1 = UniqueId.newRandom();
	UniqueId randomUniqueId2 = UniqueId.newRandom();
	UniqueId randomUniqueId3 = UniqueId.newRandom();
	UniqueId randomUniqueId4 = UniqueId.newRandom();
	UniqueId randomUniqueId1a;
	UniqueId randomUniqueId2a;
	UniqueId randomUniqueId3a;
	UniqueId randomUniqueId4a;
	int[] bytes1;
	int[] bytes2;
	int[] bytes3;
	int[] bytes4;
	int[] randomBytes1;
	int[] randomBytes2;
	int[] randomBytes3;
	int[] randomBytes4;

	//UniqueId2.set(UniqueId.NBITS - 1);
	//UniqueId3.set(UniqueId.NBITS - 2);
	//UniqueId4.set(UniqueId.NBITS - 3);

	bytes1 = uniqueId1.getBits();
	bytes2 = uniqueId2.getBits();
	bytes3 = uniqueId3.getBits();
	bytes4 = uniqueId4.getBits();

	assertEquals("Failed to convert UniqueId to Bytes", uniqueId1.toString(), new UniqueId(bytes1).toString());
	assertEquals("Failed to convert UniqueId to Bytes", uniqueId2.toString(), new UniqueId(bytes2).toString());
	assertEquals("Failed to convert UniqueId to Bytes", uniqueId3.toString(), new UniqueId(bytes3).toString());
	assertEquals("Failed to convert UniqueId to Bytes", uniqueId4.toString(), new UniqueId(bytes4).toString());

	uniqueId1a = new UniqueId(bytes1);
	uniqueId2a = new UniqueId(bytes2);
	uniqueId3a = new UniqueId(bytes3);
	uniqueId4a = new UniqueId(bytes4);

	assertEquals("Failed to convert Bytes to UniqueId", uniqueId1, uniqueId1a);
	assertEquals("Failed to convert Bytes to UniqueId", uniqueId2, uniqueId2a);
	assertEquals("Failed to convert Bytes to UniqueId", uniqueId3, uniqueId3a);
	assertEquals("Failed to convert Bytes to UniqueId", uniqueId4, uniqueId4a);

	randomBytes1 = randomUniqueId1.getBits();
	randomBytes2 = randomUniqueId2.getBits();
	randomBytes3 = randomUniqueId3.getBits();
	randomBytes4 = randomUniqueId4.getBits();
	
	assertEquals("Failed to convert UniqueId to Bytes", randomUniqueId1.toString(), new UniqueId(randomBytes1).toString());
	assertEquals("Failed to convert UniqueId to Bytes", randomUniqueId2.toString(), new UniqueId(randomBytes2).toString());
	assertEquals("Failed to convert UniqueId to Bytes", randomUniqueId3.toString(), new UniqueId(randomBytes3).toString());
	assertEquals("Failed to convert UniqueId to Bytes", randomUniqueId4.toString(), new UniqueId(randomBytes4).toString());

	randomUniqueId1a = new UniqueId(randomBytes1);
	randomUniqueId2a = new UniqueId(randomBytes2);
	randomUniqueId3a = new UniqueId(randomBytes3);
	randomUniqueId4a = new UniqueId(randomBytes4);

	assertEquals("Failed to convert Bytes to UniqueId", randomUniqueId1, randomUniqueId1a);
	assertEquals("Failed to convert Bytes to UniqueId", randomUniqueId2, randomUniqueId2a);
	assertEquals("Failed to convert Bytes to UniqueId", randomUniqueId3, randomUniqueId3a);
	assertEquals("Failed to convert Bytes to UniqueId", randomUniqueId4, randomUniqueId4a);
    }

    // This test doesn't tell us anything about the bits set after the position.  Since those
    // bits are random, I don't have a good way to test for that.  You'll have to use your
    // eyeballs for that, but the automated test ensures that it's atleast refreshing the
    // correct kbucket.  Eyeballs should only be necessary when you've rewritten the algorithm,
    // otherwise the automated section is good enough.
    @Test
    public void testGetRandomUniqueIdFromBucketPosition()
    {
        if (!RUN_CORRECTNESS_TESTS) return;
        UniqueId        myUniqueId = UniqueId.newRandom();
        UniqueId        random;
        int[]           xord = new int[UniqueId.LENGTH];
        int[]           myUniqueIdBits;
        int[]           randomBits;
	int             position;
        DHT dht;
        
        position       = rand.nextInt(UniqueId.NBITS);  // 0 - 159
        dht            = new DHT(this, myUniqueId);
        random         = dht.getRandomUniqueIdFromKBucket(position);
        randomBits     = random.bits;
        myUniqueIdBits = myUniqueId.bits;

        for (int i = 0; i < UniqueId.LENGTH; i++)        
            xord[i] = randomBits[i] ^ myUniqueIdBits[i];        

        //eyeballs section
        //System.out.println("\nMyUniqueId: " + myUniqueId);
        //System.out.println(  "RandomId  : " + random);
        //System.out.println(  "XOR'd     : " + new UniqueId(xord));
        //System.out.print  (  "            "); for (int i = 0; i < position; i++) System.out.print(" ");
        //System.out.println("^"); System.out.println("Arrow points to position " + position + ".");

        // automated section
        int index  = position >>> 5;
        int offset = position & UniqueId.TYPE_SIZE_M1;
        int temp   = xord[index];
        int sum    = 0;
        int expected;
        int result;

        temp |= (temp >>>  1);
        temp |= (temp >>>  2);
        temp |= (temp >>>  4);
        temp |= (temp >>>  8);
        temp |= (temp >>> 16);
        temp -= (temp >>>  1);
                
        expected = 1 << (UniqueId.TYPE_SIZE_M1 - offset);
        result   = temp;
        assertEquals("Position bit at " + position + " in index is correctly set for the integer at index " + index + ".",
                expected, result);

        temp = index;
        while (--temp >= 0) sum += xord[temp];
        expected = 0;
        result = sum;
        assertEquals("All bits before position " + position + " were the same as myUniqueId." + index + ".",
                expected, result);
    }

    public static void main(String[] args)
    {
	org.junit.runner.JUnitCore.main(TestDHT.class.getName());
    }
}